<template>
  <div id="app">
    <video id="localVideo" autoplay playsinline></video>
    <video id="remoteVideo" autoplay playsinline></video>
  </div>
</template>

<script>
export default {
  data() {
    return {};
  },
  // All lifecycle hooks are called with their 'this' context pointing to the Vue instance invoking it.
  // 在Vue所有的生命周期钩子方法（如created，mounted， updated以及destroyed）里使用this，this指向调用它的Vue实例。
  mounted() {
    this.init();
  },
  methods: {
    /* 
    ※  对于普通函数（包括匿名函数），this指的是直接的调用者，在非严格模式下，如果没有直接调用者，this指的是window。showMessage1()里setTimeout使用了匿名函数，this指向window。
    ※  箭头函数是没有自己的this，在它内部使用的this是由它定义的宿主对象决定。showMessage2()里定义的箭头函数宿主对象为vue实例，所以它里面使用的this指向vue实例。
    
    注：
    普通函数的this是由动态作用域决定，它总指向于它的直接调用者。具体可以分为以下四项：
      -- this总是指向它的直接调用者， 例如 obj.func() ,那么func()里的this指的是obj。
      -- 在默认情况(非严格模式,未使用 'use strict')，如果函数没有直接调用者，this为window
      -- 在严格模式下,如果函数没有直接调者，this为undefined
      -- 使用call,apply,bind绑定的，this指的是绑定的对象
    */
    init() {
      const localVideo = document.querySelector("#localVideo");
      // const remoteVideo = document.querySelector("#remoteVideo");
      const mediaStreamConstraints = { video: true };
      let localStream;
      let remoteStream;

      navigator.mediaDevices
        .getUserMedia(mediaStreamConstraints)
        .then((mediaStream) => {
          localVideo.srcObject = mediaStream;
          localStream = mediaStream;
          const videoTracks = localStream.getVideoTracks();
          const audioTracks = localStream.getAudioTracks();
          localPeerConnection.addStream(localStream);
          localPeerConnection
            .createOffer(offerOptions)
            .then(createdOffer)
            .catch(setSessionDescriptionError);
        })
        .catch((err) => {
          console.log({ err });
        });

      let servers = null;
      const offerOptions = { offerToReceiveVideo: 1 };
      const localPeerConnection = new RTCPeerConnection(servers);
      localPeerConnection.onicecandidateerror = handleConnection();
      localPeerConnection.oniceconnectionstatechange = handleConnectionChange();
      function handleConnection(event) {
        console.log("handleConnection", event);
        const peerConnection = event.target;
        const iceCandidate = event.candidate;
        if (iceCandidate) {
          const newIceCandidate = new RTCIceCandidate(iceCandidate);
          const otherPeer = getOtherPeer(peerConnection);
          otherPeer
            .addIceCandidate(newIceCandidate)
            .then(() => {
              handleConnectionSuccess(peerConnection);
            })
            .catch((error) => {
              handleConnectionFailure(peerConnection, error);
            });
        }
      }
      function handleConnectionChange(event) {
        console.log("handleConnectionChange", event);
        const peerConnection = event.target;
        console.log("peerConnection", peerConnection);
      }
      const remotePeerConnection = new RTCPeerConnection(servers);
      remotePeerConnection.onicecandidateerror = handleConnection();
      remotePeerConnection.oniceconnectionstatechange = handleConnectionChange();
      remotePeerConnection.onaddstream = gotRemoteMediaStream();
      function gotRemoteMediaStream(event) {
        console.log("gotRemoteMediaStream", event);
        const mediaStream = event.stream;
        remoteVideo.srcObject = mediaStream;
        remoteStream = mediaStream;
        console.trace("Remote peer connection received remote stream");
      }
      function handleConnectionSuccess(peerConnection) {
        // console.trace(`${getPeerName(peerConnection)} addIceCandidate success.`);
      }
      function handleConnectionFailure(peerConnection, error) {
        // console.trace(`${getPeerName(peerConnection)} failed to add ICE Candidate:\n` + `${error.toString()}.`);
      }

      function getOtherPeer(peerConnection) {
        return peerConnection === localPeerConnection
          ? remotePeerConnection
          : localPeerConnection;
      }
      function getPeerName(peerConnection) {
        return peerConnection === localPeerConnection
          ? "localPeerConnection"
          : "remotePeerConnection";
      }
      function createdOffer(description) {
        console.log(`Offer from localPeerConnection:\n`); // 10
        console.log("localPeerConnection setLocalDescription start:"); // 11
        // 更改与连接关联的本地描述。此描述指定连接本地端的属性，包括媒体格式。
        localPeerConnection
          .setLocalDescription(description)
          .then(() => {
            setLocalDescriptionSuccess(localPeerConnection);
          })
          .catch(setSessionDescriptionError);

        console.log("remotePeerConnection setRemoteDescription start."); // 12
        // 将指定的会话描述设置为远程对等方的当前提议或答案。描述指定连接的远程端的属性，包括媒体格式。
        remotePeerConnection
          .setRemoteDescription(description)
          .then(() => {
            setRemoteDescriptionSuccess(remotePeerConnection);
          })
          .catch(setSessionDescriptionError);
        console.log("remotePeerConnection createAnswer start."); // 13
        // 在 WebRTC 连接的提议/答案协商期间从远程对等方接收到的提议RTCPeerConnection创建SDP答案。
        remotePeerConnection
          .createAnswer()
          .then(createdAnswer)
          .catch(setSessionDescriptionError);
      }

      function createdAnswer(description) {
        console.log(`Answer from remotePeerConnection:\n`); // 14

        console.log("remotePeerConnection setLocalDescription start."); // 15
        remotePeerConnection
          .setLocalDescription(description)
          .then(() => {
            setLocalDescriptionSuccess(remotePeerConnection);
          })
          .catch(setSessionDescriptionError);

        console.log("localPeerConnection setRemoteDescription start."); // 16
        localPeerConnection
          .setRemoteDescription(description)
          .then(() => {
            setRemoteDescriptionSuccess(localPeerConnection);
          })
          .catch(setSessionDescriptionError);
      }
      function setSessionDescriptionError() {}
    },
  },
};
</script>
<style scoped>
video {
  width: 300px;
  height: 200px;
  border: 1px solid red;
}
</style>
